package mysql

import (
	"errors"
	"fmt"
	"gitee.com/captials-team/ubdframe/src/domain/configstc"
	"gitee.com/captials-team/ubdframe/src/domain/dto"
	"gitee.com/captials-team/ubdframe/src/domain/dto/paginate"
	"gitee.com/captials-team/ubdframe/src/domain/interfaces"
	"gitee.com/captials-team/ubdframe/src/domain/models"
	mysqlClients "gitee.com/captials-team/ubdframe/src/infrastructure/clients/mysql"
	"gitee.com/captials-team/ubdframe/src/infrastructure/dao"
	"gorm.io/gorm"
	"strings"
)

func NewMetricStatDataDao(conf configstc.DBConfig) *MetricStatDataDao {
	return &MetricStatDataDao{
		db:                mysqlClients.NewGormDB(conf),
		conf:              conf,
		SplitTableDaoHelp: dao.SplitTableDaoHelp{Model: new(models.MetricStatData), TablePrefix: conf.TablePrefix},
	}
}

type MetricStatDataDao struct {
	db   *gorm.DB
	conf configstc.DBConfig

	dao.SplitTableDaoHelp
	GormDaoHelp
	metricDaoHelp
}

func (dao *MetricStatDataDao) Search(search *dto.SearchMetricStatDto, pa *paginate.Pager) ([]*models.MetricStatValueData, *paginate.Pager, error) {
	if search == nil {
		search = &dto.SearchMetricStatDto{}
	}
	if pa == nil {
		pa = &paginate.Pager{}
	}
	pa.Correct()
	table := dao.SplitTable(search.MetricId, "")
	db := dao.db.Table(table)

	var list []*models.MetricStatValueData

	if search.MetricId > 0 {
		db = db.Where("metric_id=?", search.MetricId)
	}
	if search.StatType != "" {
		db = db.Where("stat_type=?", search.StatType)
	}
	if search.StartTime > 0 {
		db = db.Where("timestamp>=?", search.StartTime)
	}
	if search.EndTime > 0 {
		db = db.Where("timestamp<=?", search.EndTime)
	}

	fields := []string{
		"id",
		"stat_type",
		"metric_id",
		"timestamp",
		dao.DataTypeField(search.DataType) + " as value",
	}

	if ret := db.Count(&pa.Total); ret.Error != nil {
		return list, pa, ret.Error
	}

	db = db.Order("id desc").Select(fields)
	if ret := db.Limit(pa.Size).Offset(pa.Offset()).Find(&list); ret.Error != nil {
		return list, pa, ret.Error
	}
	pa.Correct()
	return list, pa, nil
}

func (dao *MetricStatDataDao) DataTypeField(dataType string) string {
	switch dataType {
	case "last":
		return "value_last"
	case "min":
		return "value_min"
	case "max":
		return "value_max"
	case "avg":
		return "value_avg"
	case "sum":
		return "value_sum"
	case "count":
		return "value_count"
	}
	//默认查全部
	return "value_last"
}

func (dao *MetricStatDataDao) Query(param *dto.QueryMetricStatDto) (*models.MetricStatValueData, error) {
	table := dao.SplitTable(param.MetricId, "")
	db := dao.db.Table(table)

	var find models.MetricStatValueData
	if param.MetricId > 0 {
		db = db.Where("metric_id", param.MetricId)
	}
	if param.StatType != "" {
		db = db.Where("stat_type=?", param.StatType)
	}
	if param.Timestamp > 0 {
		db = db.Where("timestamp=?", param.Timestamp)
	}
	if param.StartTime > 0 {
		db = db.Where("timestamp>=?", param.StartTime)
	}
	if param.EndTime > 0 {
		db = db.Where("timestamp<=?", param.EndTime)
	}
	fields := []string{
		"id",
		"stat_type",
		"metric_id",
		"timestamp",
		dao.DataTypeField(param.DataType) + " as value",
	}

	db = db.Select(fields).Order("id DESC")

	ret := db.First(&find)
	if errors.Is(ret.Error, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	if ret.Error != nil {
		return nil, ret.Error
	}

	return &find, nil
}

func (dao *MetricStatDataDao) QuerySum(search *dto.SearchMetricStatDto) (*models.MetricValueData, error) {
	table := dao.SplitTable(search.MetricId, "")
	db := dao.db.Table(table)

	var find models.MetricValueData
	if search.MetricId > 0 {
		db = db.Where("metric_id", search.MetricId)
	}
	if search.StatType != "" {
		db = db.Where("stat_type=?", search.StatType)
	}
	if search.Timestamp > 0 {
		db = db.Where("timestamp=?", search.Timestamp)
	}
	if search.StartTime > 0 {
		db = db.Where("timestamp>=?", search.StartTime)
	}
	if search.EndTime > 0 {
		db = db.Where("timestamp<=?", search.EndTime)
	}
	fields := []string{
		"metric_id",
		fmt.Sprintf("SUM(%s) as value", dao.DataTypeField(search.DataType)),
	}

	db = db.Select(fields).Order("id DESC")

	ret := db.First(&find)
	if errors.Is(ret.Error, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	if ret.Error != nil {
		return nil, ret.Error
	}

	return &find, nil
}

func (dao *MetricStatDataDao) MergeAdd(adds ...*models.MetricStatData) (int64, error) {
	var sql []string
	for _, v := range adds {
		sql = append(sql, dao.SQLForWriteMetricStatData(dao.SplitTable(v.MetricId, ""), []*models.MetricStatData{v}, true)...)
	}

	ret := dao.db.Exec(strings.Join(sql, ";"))
	return ret.RowsAffected, ret.Error
}

func (dao *MetricStatDataDao) CoverAdd(adds ...*models.MetricStatData) (int64, error) {
	var sql []string
	for _, v := range adds {
		sql = append(sql, dao.SQLForWriteMetricStatData(dao.SplitTable(v.MetricId, ""), []*models.MetricStatData{v}, false)...)
	}

	ret := dao.db.Exec(strings.Join(sql, ";"))
	return ret.RowsAffected, ret.Error
}

func (dao *MetricStatDataDao) InitSplitTable(splitKey string) error {
	return dao.BeInitSubTable(dao.db, dao.SplitTable(0, splitKey), new(models.MetricStatData))
}

func (dao *MetricStatDataDao) Migrate() (int64, error) {
	return 1, dao.db.Table(dao.SplitTable(0, "")).Migrator().AutoMigrate(dao.Model)
}

func (dao *MetricStatDataDao) DB() *gorm.DB {
	return dao.db
}

func (dao *MetricStatDataDao) Use(db *gorm.DB) interfaces.ItfMetricStatDataDao {
	return &MetricStatDataDao{db: db}
} //使用db
